//
// Created by syj on 2023/7/26.
//
#include <sstream>
#include <fstream>
#include <regex>
#include <errno.h>
#include "logger.h"

using namespace mlogimpl;

#define MAX_LOGGER_SIZE (1<<10)
#define MAX_THREAD_SIZE (1)

Logger::Logger()
:pLogger([]()->SpdLogPtr{
    auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
    console_sink->set_level(spdlog::level::trace);
    console_sink->set_pattern("[%Y-%m-%d %H:%M:%S.%f] [%^%l%$] [%P:%t] [%s:%#] [%!] %v");
    return std::make_shared<spdlog::logger>(spdlog::logger("multi_sink", console_sink));
}())
{
    spdlog::set_level(spdlog::level::trace); //开启spdlog宏打印的trace debug等级
    pLogger->set_level(spdlog::level::trace);
//    spdlog::register_logger(pLogger);
}

Logger::~Logger() noexcept
{
    spdlog::drop_all();
    /*if (nullptr != node)
    {
//        cJSON_free(node);
        cJSON_Delete(node);
    }*/
}

#if 0
void Logger::Init(const std::string file, const std::string name)
{
    if (access(file.c_str(), F_OK) == -1)
    {
        SPDLOG_TRACE("no this file!");
        return;
    }
    std::ifstream infile(file, std::ios::in | std::ios::binary);
    std::string read_file;
    if (infile.is_open())
    {
        std::stringstream ss;
        ss << infile.rdbuf();
        read_file = ss.str();
        infile.close();
    }
    if (read_file.empty())
    {
        SPDLOG_TRACE("read file failed!");
        return;
    }
    node = cJSON_Parse(read_file.c_str()); //解析成json形式
    cJSON * json_logger = cJSON_GetObjectItem(node,"logger"); //获取键值内容
    if (nullptr != json_logger)
    {
        int array_size = cJSON_GetArraySize(json_logger);
        SPDLOG_TRACE("have {} process logger config!", array_size);
        cJSON * item, *module_name;
        for (int i = 0; i < array_size; ++i)
        {
            item = cJSON_GetArrayItem(json_logger,i);
            if (nullptr != item)
            {
//                char * p = cJSON_PrintUnformatted(item);
                module_name = cJSON_GetObjectItem(item, "module_name");
                if ((nullptr != module_name) && (0 == strcmp(name.c_str(),module_name->valuestring)))
                {
                    cJSON *console_level,*file_level,*root_path,*max_file_size,*max_file_num;
                    SPDLOG_INFO("{} :{}!",module_name->string,module_name->valuestring);
                    console_level = cJSON_GetObjectItem(item,"console_level");
                    file_level = cJSON_GetObjectItem(item,"file_level");
                    root_path = cJSON_GetObjectItem(item,"root_path");
                    max_file_size = cJSON_GetObjectItem(item,"max_file_size");
                    max_file_num = cJSON_GetObjectItem(item,"max_file_num");

                    /* 重新设置新的 logger */
                    if ((nullptr != console_level) && (nullptr != root_path ) && (nullptr != file_level )
                        && (nullptr != max_file_size ) && (nullptr != max_file_num ))
                    {
                        SPDLOG_INFO("console_level:{}, file_level:{},root_path:{},max_file_size:{},max_file_num:{}"
                                ,console_level->valuestring,file_level->valuestring
                                ,root_path->valuestring,max_file_size->valuestring,max_file_num->valueint
                                );
                        /* 异步就一定需要线程或线程池+日志队列,这里给MAX_THREAD_SIZE个线程，队列 1024 个字节 */
                        spdlog::init_thread_pool(MAX_LOGGER_SIZE,MAX_THREAD_SIZE);
                        auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
                        SetLogLevel(console_sink,console_level->valuestring);
                        console_sink->set_pattern("[%Y-%m-%d %H:%M:%S.%f] [%^%l%$] [%P:%t] [%s:%#] [%!] %v");

                        auto max_size = ParseFileSize(max_file_size->valuestring);
                        auto file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(
                                std::string(root_path->valuestring) + "/" + std::string(module_name->valuestring) + "_log.txt",
                                max_size,
                                max_file_num->valueint
                                );
                        SetLogLevel(file_sink, file_level->valuestring);

                        file_sink->set_pattern("[%Y-%m-%d %H:%M:%S.%F] [%^%l%$] [%P:%t] [%s:%#] [%!] %v");

                        std::vector<spdlog::sink_ptr> sinks{console_sink, file_sink};

                        pLogger.reset(
                                new spdlog::async_logger(
                                        "multi_sink",
                                        sinks.begin(),
                                        sinks.end(),
                                        spdlog::thread_pool(),
                                        spdlog::async_overflow_policy::block
                                        )
                                );
                    }
                }
            }
        }
    }
}
#else
void Logger::Init(const LoggerConfig & config)
{
    try {
        /* 异步就一定需要线程或线程池+日志队列,这里给MAX_THREAD_SIZE个线程，队列 1024 个字节 */
        spdlog::init_thread_pool(MAX_LOGGER_SIZE,MAX_THREAD_SIZE);
        auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
        SetLogLevel(console_sink,config.console_level);
        console_sink->set_pattern("[%Y-%m-%d %H:%M:%S.%f] [%^%l%$] [%P:%t] [%s:%#] [%!] %v");
        int max_file_num = 0;
        if (1 >= config.file_num)
        {
            max_file_num = 1;
        }
        else
        {
            max_file_num = config.file_num - 1;
        }

        auto max_size = ParseFileSize(std::string(config.file_size));
        auto file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(
                std::string(config.root_path) + "/" + std::string(config.module_name) + "_log.txt",
                max_size,
                max_file_num
        );
        SetLogLevel(file_sink, config.file_level);

        file_sink->set_pattern("[%Y-%m-%d %H:%M:%S.%F] [%^%l%$] [%P:%t] [%s:%#] [%!] %v");

        std::vector<spdlog::sink_ptr> sinks{console_sink, file_sink};

        pLogger.reset(
                new spdlog::async_logger(
                        "multi_sink",
                        sinks.begin(),
                        sinks.end(),
                        spdlog::thread_pool(),
                        spdlog::async_overflow_policy::block
                )
        );
    }
    catch (spdlog::spdlog_ex & ex)
    {
        SPDLOG_ERROR("Logger::Init failed,message:{}", ex.what());
    }
}
#endif
#if 0
void Logger::SetLogLevel(const SinkPtr &sink, const std::string & log_level)
{
    if (log_level.empty()) { sink->set_level(spdlog::level::off); return; }
    if (std::string::npos != log_level.find("tra"))
    { sink->set_level(spdlog::level::trace); }
    else if (std::string::npos != log_level.find("deb"))
    { sink->set_level(spdlog::level::debug); }
    else if (std::string::npos != log_level.find("inf"))
    { sink->set_level(spdlog::level::info); }
    else if (std::string::npos != log_level.find("war"))
    { sink->set_level(spdlog::level::warn); }
    else if (std::string::npos != log_level.find("err"))
    { sink->set_level(spdlog::level::err); }
    else if (std::string::npos != log_level.find("cri"))
    { sink->set_level(spdlog::level::critical); }
    else { sink->set_level(spdlog::level::off); }
}
#else
void Logger::SetLogLevel(const SinkPtr & sink, const LoggerLevel & level)
{
    switch (level)
    {
        case LoggerLevel::trace:
            sink->set_level(spdlog::level::trace);
            break;
        case LoggerLevel::debug:
            sink->set_level(spdlog::level::debug);
            break;
        case LoggerLevel::info:
            sink->set_level(spdlog::level::info);
            break;
        case LoggerLevel::warn:
            sink->set_level(spdlog::level::warn);
            break;
        case LoggerLevel::err:
            sink->set_level(spdlog::level::err);
            break;
        case LoggerLevel::critical:
            sink->set_level(spdlog::level::critical);
            break;
        case LoggerLevel::off:
            sink->set_level(spdlog::level::off);
            break;
        default:
            sink->set_level(spdlog::level::warn);
            break;
    }
}
#endif
size_t Logger::ParseFileSize(const std::string & file_size)
{
//    std::regex rx("\\s*(\\d+|\\d+[.]|\\d?[.]\\d+)\\s*((k|K|M|m|G|g)[Bb]?)\\s*");
    std::regex rx("\\s*(\\d+|\\d+[.]|\\d?[.]\\d+)\\s*((k|K|M|m|G|g|\\s*|b|B)[Bb]?)");
    std::smatch m;
    if (!std::regex_match(file_size, m,rx))
    {
        throw std::runtime_error("Unable to parse '" + file_size + "' as size.");
    }
//    for (int i = 0; i < m.size(); ++i) {}
    double d = std::stod(m[1].str());
    size_t mult = 1;
    if ("" == m[3] || "b" == m[3] || "B" == m[3])
    {
        mult = 1;
    }
    else if ("k" == m[3] || "K" == m[3])
    {
        mult = 1<<10;
    }
    else if ("m" == m[3] || "M" == m[3])
    {
        mult = 1<<20;
    }
    else if ("g" == m[3] || "G" == m[3])
    {
        mult = 1<<30;
    }
    else
    {
        throw std::logic_error("Unable prefix '" + m[2].str() + "'.");
    }
    return d * mult;
}
